﻿using System;
namespace MutsiEngine
{
    public struct Vector3
    {
        public static Vector3 Zero { get { return new Vector3(0, 0, 0); } }
        public static Vector3 One { get { return new Vector3(1, 1, 1); } }
        public static Vector3 Up { get { return new Vector3(0, 1, 0); } }
        public static Vector3 UnitX { get { return new Vector3(1.0f, 0.0f, 0.0f); } }
        public static Vector3 UnitY { get { return new Vector3(0.0f, 1.0f, 0.0f); } }
        public static Vector3 UnitZ { get { return new Vector3(0.0f, 0.0f, 1.0f); } }

        public float X;
        public float Y;
        public float Z;

        public Vector3(float X, float Y, float Z)
        {
            this.X = X;
            this.Y = Y;
            this.Z = Z;
        }

        private float getLength()
        {
            return (float)Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
        }

        public void Normalize()
        {
            float length = getLength();
            this.X = X / length;
            this.Y = Y / length;
            this.Z = Z / length;
        }

        public static Vector3 Normalize(Vector3 v)
        {
            float length = v.getLength();
            return new Vector3(v.X / length, v.Y / length, v.Z / length);
        }

        public static Vector3 CrossProduct(Vector3 v1, Vector3 v2)
        {
            return
            (
               new Vector3
               (
                  v1.Y * v2.Z - v1.Z * v2.Y,
                  v1.Z * v2.X - v1.X * v2.Z,
                  v1.X * v2.Y - v1.Y * v2.X
               )
            );
        }

        public Vector3 CrossProduct(Vector3 other)
        {
            return CrossProduct(this, other);
        }

        public static float DotProduct(Vector3 v1, Vector3 v2)
        {
            return
            (
               v1.X * v2.X +
               v1.Y * v2.Y +
               v1.Z * v2.Z
            );
        }

        public float DotProduct(Vector3 other)
        {
            return DotProduct(this, other);
        }

        public static Vector3 Interpolate(Vector3 v1, Vector3 v2, float fraction)
        {
            float frac = MutsiMath.Clamp<float>(fraction, 0, 1);
            return new Vector3(
                v1.X * (1 - frac) + v2.X * frac,
                v1.Y * (1 - frac) + v2.Y * frac,
                v1.Z * (1 - frac) + v2.Z * frac
                );
        }
        public Vector3 Interpolate(Vector3 other, float fraction)
        {
            return Interpolate(this, other, fraction);
        }

        public static float Distance(Vector3 v1, Vector3 v2)
        {
            return
            (
               (float)Math.Sqrt
               (
                   (v1.X - v2.X) * (v1.X - v2.X) +
                   (v1.Y - v2.Y) * (v1.Y - v2.Y) +
                   (v1.Z - v2.Z) * (v1.Z - v2.Z)
               )
            );
        }
        public float Distance(Vector3 other)
        {
            return Distance(this, other);
        }

        public static float Angle(Vector3 v1, Vector3 v2)
        {
            return
            (
               (float)Math.Acos
               (
                  Normalize(v1).DotProduct(Normalize(v2))
               )
            );
        }

        public float Angle(Vector3 other)
        {
            return Angle(this, other);
        }

        public static Vector3 operator +(Vector3 vec1, Vector3 vec2)
        {
            float x = vec1.X + vec2.X;
            float y = vec1.Y + vec2.Y;
            float z = vec1.Z + vec2.Z;
            return new Vector3(x, y, z);
        }

        public static Vector3 operator -(Vector3 vec1, Vector3 vec2)
        {
            float x = vec1.X - vec2.X;
            float y = vec1.Y - vec2.Y;
            float z = vec1.Z - vec2.Z;
            return new Vector3(x, y, z);
        }

        public static Vector3 operator -(Vector3 vec1)
        {
            return new Vector3(-vec1.X, -vec1.Y, -vec1.Z);
        }

        public static Vector3 operator *(Vector3 vec1, Vector3 vec2)
        {
            float x = vec1.X * vec2.X;
            float y = vec1.Y * vec2.Y;
            float z = vec1.Z * vec2.Z;
            return new Vector3(x, y, z);
        }
        public static Vector3 operator *(Vector3 vec1, float scaler)
        {
            float x = vec1.X * scaler;
            float y = vec1.Y * scaler;
            float z = vec1.Z * scaler;
            return new Vector3(x, y, z);
        }

        public static Vector3 operator /(Vector3 vec1, Vector3 vec2)
        {
            float x = vec1.X / vec2.X;
            float y = vec1.Y / vec2.Y;
            float z = vec1.Z / vec2.Z;
            return new Vector3(x, y, z);
        }
        public static Vector3 operator /(Vector3 vec1, float scaler)
        {
            float x = vec1.X / scaler;
            float y = vec1.Y / scaler;
            float z = vec1.Z / scaler;
            return new Vector3(x, y, z);
        }
        public static bool operator ==(Vector3 vec1, Vector3 vec2)
        {
            return (vec1.X == vec2.X) && (vec1.Y == vec2.Y) && (vec1.Z == vec2.Z);
        }
        public static bool operator !=(Vector3 vec1, Vector3 vec2)
        {
            return !(vec1.X == vec2.X) && (vec1.Y == vec2.Y) && (vec1.Z == vec2.Z);
        }

        public override bool Equals(object other)
        {
            // Check object other is a Vector3 object
            if (other is Vector3)
            {
                // Convert object to Vector3
                Vector3 otherVector = (Vector3)other;

                // Check for equality
                return otherVector == this;
            }
            else
            {
                return false;
            }
        }

        public bool Equals(Vector3 other)
        {
            return other == this;
        }

        public override int GetHashCode()
        {
            return
            (
               (int)((X + Y + Z) % Int32.MaxValue)
            );
        }

        //Console.Writeline the vector3
        public void Print()
        {
            Console.WriteLine("Vector3");
            Console.WriteLine("X= " + X);
            Console.WriteLine("Y= " + Y);
            Console.WriteLine("Z= " + Z);
        }
    }
}
